package org.clockworkmages.games.anno1186;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.clockworkmages.games.anno1186.dao.GameObjectsList;
import org.clockworkmages.games.anno1186.dao.ImageIconDescriptor;
import org.clockworkmages.games.anno1186.gui.GameUiUtil;
import org.clockworkmages.games.anno1186.model.Fetish;
import org.clockworkmages.games.anno1186.model.character.Mutation;
import org.clockworkmages.games.anno1186.model.character.NonPlayerCharacter;
import org.clockworkmages.games.anno1186.model.character.Skill;
import org.clockworkmages.games.anno1186.model.character.Stat;
import org.clockworkmages.games.anno1186.model.effect.Condition;
import org.clockworkmages.games.anno1186.model.effect.Effect;
import org.clockworkmages.games.anno1186.model.effect.Perk;
import org.clockworkmages.games.anno1186.model.item.EquipmentSlot;
import org.clockworkmages.games.anno1186.model.item.Item;
import org.clockworkmages.games.anno1186.model.map.tactical.GameAreaDO;
import org.clockworkmages.games.anno1186.model.map.tactical.SimpleTileObject;
import org.clockworkmages.games.anno1186.model.map.tactical.TileObject;
import org.clockworkmages.games.anno1186.model.option.GenericOption;
import org.clockworkmages.games.anno1186.model.option.Option;
import org.clockworkmages.games.anno1186.model.situation.ExplorableGameEvent;
import org.clockworkmages.games.anno1186.model.situation.GenericSituation;
import org.clockworkmages.games.anno1186.model.situation.Situation;

public class GameData {

	private Map<String, List<ExplorableGameEvent>> eventPools = new HashMap<String, List<ExplorableGameEvent>>();

	private Map<String, Situation> situations = new HashMap<String, Situation>();

	private Map<String, Option> namedOptions = new HashMap<String, Option>();
	private List<GenericOption> locatedOptions = new ArrayList<GenericOption>();

	private Map<String, Stat> stats = new HashMap<String, Stat>();
	private Map<String, Skill> skills = new HashMap<String, Skill>();

	private Map<String, Perk> perks = new HashMap<String, Perk>();

	private Map<String, Condition> conditions = new HashMap<String, Condition>();

	private Map<String, EquipmentSlot> equipmentSlots = new LinkedHashMap<String, EquipmentSlot>();

	private Map<String, Mutation> mutations = new HashMap<String, Mutation>();

	private Map<String, String> optionImageIconDescriptors = new HashMap<String, String>();

	private Map<String, Item> items = new HashMap<String, Item>();
	private Map<String, NonPlayerCharacter> npcs = new HashMap<String, NonPlayerCharacter>();
	private Map<String, Fetish> fetishes = new HashMap<String, Fetish>();

	private List<Effect> baseEffects = new ArrayList<Effect>();

	private String gameStartSituation;

	private Map<String, TileObject> tileObjects = new HashMap<String, TileObject>();
	private Map<String, GameAreaDO> gameAreas = new HashMap<String, GameAreaDO>();

	public void merge(GameObjectsList gameObjects) {
		for (ImageIconDescriptor imageIconDescriptor : gameObjects.getIcons()) {
			optionImageIconDescriptors.put(imageIconDescriptor.getId(),
					imageIconDescriptor.getPath());
		}
		for (SimpleTileObject tileObject : gameObjects.getTileObjects()) {
			this.tileObjects.put(tileObject.getId(), tileObject);
		}
		for (GameAreaDO gameAreaDO : gameObjects.getGameAreas()) {
			this.gameAreas.put(gameAreaDO.getId(), gameAreaDO);
		}
		for (Stat stat : gameObjects.getStats()) {
			stats.put(stat.getId(), stat);
		}
		for (Skill skill : gameObjects.getSkills()) {
			skills.put(skill.getId(), skill);
		}
		for (Perk perk : gameObjects.getPerks()) {
			perks.put(perk.getId(), perk);
		}
		for (Condition condition : gameObjects.getConditions()) {
			conditions.put(condition.getId(), condition);
		}
		for (EquipmentSlot equipmentSlot : gameObjects.getEquipmentSlots()) {
			equipmentSlots.put(equipmentSlot.getId(), equipmentSlot);
		}
		for (Item item : gameObjects.getItems()) {
			items.put(item.getId(), item);
		}
		for (NonPlayerCharacter npc : gameObjects.getNpcs()) {
			npcs.put(npc.getId(), npc);
		}
		for (Fetish fetish : gameObjects.getFetishes()) {
			fetishes.put(fetish.getId(), fetish);
		}
		for (Mutation mutation : gameObjects.getMutations()) {
			mutations.put(mutation.getId(), mutation);
		}
		this.baseEffects.addAll(gameObjects.getBaseEffects());
		for (GenericOption option : gameObjects.getOptions()) {
			if (option.getId() != null) {
				namedOptions.put(option.getId(), option);
			}
			if (option.getSituationId() != null) {
				locatedOptions.add(option);
			}
		}
		for (GenericSituation situation : gameObjects.getSituations()) {
			if (situation.getId() != null) {
				situations.put(situation.getId(), situation);
			}
			if (situation.getAreaType() != null) {
				String situationPoolId = situation.getAreaType();
				List<ExplorableGameEvent> pool = eventPools
						.get(situationPoolId);
				if (pool == null) {
					pool = new ArrayList<ExplorableGameEvent>();
					eventPools.put(situationPoolId, pool);
				}
				pool.add(situation);
			}
		}

		if (gameObjects.getGameStartSituation() != null) {
			this.gameStartSituation = gameObjects.getGameStartSituation();
		}

	}

	/**
	 * Post process the named options (options whose id can be referenced by a
	 * placeholder-option of some Situations) and located Options (options that
	 * are defined outside of the Situation's original file but that should be
	 * added to that Situation in order to extend it).
	 */
	public void postMerge() {
		// process named Options:
		Map<Integer, String> replacements = new HashMap<Integer, String>();
		for (Situation situation : situations.values()) {
			replacements.clear();
			List<Option> options = situation.getOptions();
			for (int i = 0; i < situation.getOptions().size(); i++) {
				Option option = situation.getOptions().get(i);
				if (option instanceof GenericOption) {
					String reference = ((GenericOption) option).getRef();
					if (reference != null) {
						replacements.put(i, reference);
					}
				}
			}
			for (Entry<Integer, String> entry : replacements.entrySet()) {
				int index = entry.getKey();
				String reference = entry.getValue();
				Option namedOption = this.namedOptions.get(reference);
				options.remove(index);
				options.add(index, namedOption);
			}
		}

		// process located Options:
		for (GenericOption option : this.locatedOptions) {
			String situationId = option.getSituationId();
			Situation situation = situations.get(situationId);
			if (situation == null) {
				GameUiUtil
						.logError("Invalid Situation reference in a locatedOption: "
								+ situationId);
			} else {
				situation.getOptions().add(option);
			}

		}
	}

	public Map<String, List<ExplorableGameEvent>> getEventPools() {
		return eventPools;
	}

	public Map<String, Situation> getSituations() {
		return situations;
	}

	public Map<String, Option> getOptions() {
		return namedOptions;
	}

	public Map<String, EquipmentSlot> getEquipmentSlots() {
		return equipmentSlots;
	}

	public Map<String, Perk> getPerks() {
		return perks;
	}

	public String getGameStartSituation() {
		return gameStartSituation;
	}

	public Map<String, Stat> getStats() {
		return stats;
	}

	public Map<String, Skill> getSkills() {
		return skills;
	}

	public Map<String, Mutation> getMutations() {
		return mutations;
	}

	public Map<String, String> getOptionImageIconDescriptors() {
		return optionImageIconDescriptors;
	}

	public Map<String, Condition> getConditions() {
		return conditions;
	}

	public Map<String, Item> getItems() {
		return items;
	}

	public List<Effect> getBaseEffects() {
		return baseEffects;
	}

	public Map<String, NonPlayerCharacter> getNpcs() {
		return npcs;
	}

	public Map<String, Fetish> getFetishes() {
		return fetishes;
	}

	public Map<String, TileObject> getTileObjects() {
		return tileObjects;
	}

	public Map<String, GameAreaDO> getGameAreas() {
		return gameAreas;
	}

}
